﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.IO.Compression;

namespace WebDownload
{
    public partial class FormMain : Form
    {
        // Save the all link list, in parser thread, will parser the super link and add the link to this list.
        // and then, the parser thread will parser this list. to find all link in site.
        //List<string> linkList = new List<string>();
        SerializableDictionary<string, object> linkList = new SerializableDictionary<string, object>();

        // Save the all image url, when add new url, will be contains the url is not downloaded, 
        // if no, add to list and download list. 
        SerializableDictionary<string, DownloadInfo> imageList = new SerializableDictionary<string, DownloadInfo>();

        // The download image list. 
        Queue<string> downloadList = new Queue<string>();

        // The parser url list. 
        Queue<string> workList = new Queue<string>();

        // The thread list, the first is parser thread, other is download thread.
        Dictionary<int,ThreadStatus> threadList = new Dictionary<int,ThreadStatus>();


        SerializableDictionary<string, ListViewGroup> listgroupmap = new SerializableDictionary<string, ListViewGroup>();

        string baseurl = "";

        bool AppRunning = false;
        bool bIsStartWork = false;

        Point PrevLoc = new Point();
        Size PrevSize = new Size();

        List<string> addToViewList = new List<string>();

        DownloadConfig config = null;

        string CurrentConfigPath = "";

        FormProgressBar form = new FormProgressBar();

        char[] pathRep = { '|', '*', '?', '"', '<', '>' };
        char[] nameRep = { '/', '|', '\\', ':', '*', '?', '"', '<', '>' };

        

        public FormMain()
        {
            InitializeComponent();

            AppRunning = true;
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            FormWizard wiz = new FormWizard();

            if( DialogResult.OK != wiz.ShowDialog())
                Application.Exit();

            int num = Environment.ProcessorCount + 2;
            for (int i = 1; i < 10; i++)
                comboBoxThreadNum.Items.Add(i);

            comboBoxThreadNum.Text = num.ToString();

            listViewInfo.Groups.Clear();
            listViewInfo.Items.Clear();

            listViewInfo.Columns.Add("Url",listViewInfo.Width - 25);
            listViewInfo.SetGroupState(ListViewGroupState.Collapsible);

            ShowPrevArea(false);

            labelInfo.Text = "No open";

            OpenConfig(wiz.ConfigPath);

        }

        private bool isLuan(string txt)
        {
            var bytes = Encoding.UTF8.GetBytes(txt);
            //239 191 189
            for (var i = 0; i < bytes.Length; i++)
            {
                if (i < bytes.Length - 3)
                    if (bytes[i] == 239 && bytes[i + 1] == 191 && bytes[i + 2] == 189)
                    {
                        return true;
                    }
            }
            return false;
        }

        /// <summary>
        /// 获取网页源代码方法
        /// </summary>
        /// <param name="url">地址</param>
        /// <param name="charSet">指定编码，如果为空，则自动判断</param>
        /// <param name="out_str">网页源代码</param>
        public string GetHtml(string url, string charSet)
        {
            string strResult = "";
            try
            {

                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0";
                request.Headers.Add("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
                request.Headers.Add("Accept-Enconding", "gzip, deflate");
                request.KeepAlive = false;
                request.ContentType = "text/html";
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                Stream streamReceive = response.GetResponseStream();

                if (response.ToString() != "")
                {
                    string strEncoding = null;
                    int index = response.ContentType.IndexOf("charset=");
                    if (-1 != index)
                    {
                        string headencoding = response.ContentType.Substring(index + 8);
                        if (!string.IsNullOrWhiteSpace(headencoding))
                            strEncoding = headencoding;
                    }
                    else
                    {
                        strEncoding = response.CharacterSet;
                    }

                    StreamReader streamReader = null;
                    Encoding encoding = Encoding.GetEncoding(strEncoding);

                    if (response.ContentEncoding == "gzip")
                    {
                        streamReader = new StreamReader(new GZipStream(streamReceive, CompressionMode.Decompress), encoding);
                    }
                    else
                    {
                        streamReader = new StreamReader(streamReceive, encoding);
                    }

                    strResult = streamReader.ReadToEnd();
                    strEncoding = strEncoding.ToLower();

                    Encoding eUTF = Encoding.GetEncoding("utf-8");
                    Encoding eGBK = Encoding.GetEncoding("GBK");
                    if( encoding != eUTF && encoding != eGBK) //strEncoding != "utf-8" && strEncoding != "bgk" && strEncoding != "gb2312")
                    {
                        Match charSetMatch = Regex.Match(strResult, "<meta([^>]*)charset=(\")?(.*)?\"", RegexOptions.IgnoreCase | RegexOptions.Multiline);
                        if (charSetMatch.Length > 0)
                        {
                            string webCharSet = charSetMatch.Groups[3].Value.Trim().ToLower();
                            if (!string.IsNullOrWhiteSpace(webCharSet))
                            {
                                strEncoding = webCharSet;

                                var bRes = encoding.GetBytes(strResult);
                                encoding = Encoding.GetEncoding(strEncoding);
                                strResult = encoding.GetString(bRes);
                            }
                        }
                    }
                }
            }
            catch (Exception exp)
            {
                WriteLog("Download page fiald, the url is not find.error message is:" + exp.ToString() + ".WorkUrl is:" + url);
            }
            return strResult;
        }


        public void ParserLoop()
        {
            WriteLog("Parser Thread is starting. id is:" + Thread.CurrentThread.ManagedThreadId);
            while (true)
            {
                if (!AppRunning)
                    break;

                var status = threadList[Thread.CurrentThread.ManagedThreadId];
                status.isBusy = false;
                if (!bIsStartWork)
                {
                    Thread.CurrentThread.Suspend();
                }

                if (workList.Count <= 0)
                {
                    Thread.Sleep(500);
                    continue;
                }
                string workurl = "";
                lock (workList)
                {
                    if (workList.Count <= 0)
                    {
                        Thread.Sleep(500);
                        continue;
                    }
                    workurl = workList.Dequeue();
                }
                status.isBusy = true;

                // 全局相对路径,解析当前url的根路径
                string orgbaseurl = "";
                if (workurl.IndexOf('/', 8) == -1)
                {
                    orgbaseurl = workurl;
                }
                else
                {
                    orgbaseurl = workurl.Substring(0, workurl.IndexOf('/', 8));
                }

                try
                {
                    int end = -1;
                    int start = -1;
                    string pageHtml = "";
                    // 在解析到title之后,会根据title关键字分析当前页,决定是否直接跳过当前页的解析
                    bool bNextPage = false;

                    pageHtml = GetHtml(workurl, null);

                    pageHtml = pageHtml.Replace("<", "\n<");
                    string[] listtemp = Regex.Split(pageHtml, "\n", RegexOptions.IgnoreCase);

                    List<string> list = new List<string>();
                    foreach (var item in listtemp)
                    {
                        var temp = item.Replace("\t", "");
                        temp = temp.Trim();
                        if (temp.Length > 5 && (temp.IndexOf("<img") != -1 || temp.IndexOf("<a") != -1 || temp.IndexOf("<title") != -1))
                        {
                            list.Add(temp);
                        }
                    }

                    string title = "no title";


                    foreach (var item in list)
                    {
                        string splitstring = "";

                        bool bParserIMG = false;
                        if (-1 != item.IndexOf("<img", StringComparison.OrdinalIgnoreCase))
                        {
                            splitstring = "src=";
                            bParserIMG = true;
                        }
                        else if (-1 != item.IndexOf("<a", StringComparison.OrdinalIgnoreCase))
                        {
                            splitstring = "href=";
                            bParserIMG = false;
                        }
                        else if (-1 != item.IndexOf("<title", StringComparison.OrdinalIgnoreCase))
                        {
                            start = item.IndexOf("<title>", StringComparison.OrdinalIgnoreCase) + 7;
                            end = item.IndexOf(@"</title>", StringComparison.OrdinalIgnoreCase);
                            if (end == -1)
                                end = item.Length;
                            title = item.Substring(start, end - start);

                            // 根据title关键字开始过滤
                            string[] keys = new string[1] { config.TitleKeyWords };
                            if( config.TitleKeyWords.IndexOf(";") != -1 )
                                keys = config.TitleKeyWords.Split(';');

                            bool isFind = false;
                            foreach( var key in keys )
                            {
                                if (title.IndexOf(key) != -1)
                                {
                                    isFind = true;
                                    break;
                                }
                                    
                            }

                            if (isFind != true && config.workUrl != workurl)
                                bNextPage = true;

                            continue;
                        }
                        else
                        {
                            continue;
                        }

                        if (bNextPage == true)
                            break;


                        // parser the url
                        if (-1 == item.IndexOf(splitstring, StringComparison.OrdinalIgnoreCase))
                            continue;

                        string url = "";

                        string[] resu = item.Split(' ');
                        foreach (var subitem in resu)
                        {
                            if (-1 != subitem.IndexOf(splitstring, StringComparison.OrdinalIgnoreCase))
                            {
                                url = subitem.Substring(subitem.IndexOf("=") + 1);
                                break;
                            }
                        }

                        url = GetRealURL(url, orgbaseurl, workurl);
                        // 过滤javascript 和#号
                        if( url == "" )
                        {
                            continue;
                        }


                        // some a tag's url is a image. check it.
                        if (bParserIMG == false)
                        {
                            string temp = url.Substring(url.Length - 4).ToLower();
                            if (temp == "jpeg" || temp == ".jpg" || temp == ".png" || temp == ".bmp" || temp == ".gif")
                            {
                                url = url.Substring(url.LastIndexOf("://") - 4);
                                bParserIMG = true;
                            }
                        }

                        // 检查是否是当前网站的链接
                        if (!bParserIMG && url.IndexOf(baseurl, StringComparison.OrdinalIgnoreCase) == -1)//&& url.IndexOf(orgbaseurl) == -1 )
                        {
                            //WriteLog("The url is not current site:" + url);
                            continue;
                        }

                        // One Page Checked
                        if (!bParserIMG && config.OnlyCurPage != -1 )
                        {
                            string workpage = "";
                            // 索引为0的即父路径一致,寻找最后一个"/"
                            if( config.OnlyCurPage == 0 )
                            {
                                workpage = config.workUrl.Substring(0, config.workUrl.LastIndexOf('/'));
                            }
                            // 索引为1的即为当前路径直接添加_xx,寻找最后一个"_"
                            else if( config.OnlyCurPage == 1)
                            {
                                workpage = config.workUrl.Substring(0, config.workUrl.LastIndexOf('_'));
                            }

                            if (url.IndexOf(workpage) == -1)
                            {
                                continue;
                            }
                        }
                        
                        

                        if (bParserIMG)
                        {
                            if (!imageList.ContainsKey(url))
                            {
                                string samiltitle = title;
                                // 处理title
                                foreach (var split in config.pageSplit)
                                {
                                    Regex rg = new Regex(split);
                                    var res = rg.Match(title);
                                    if (res.Value != "")
                                    {
                                        samiltitle = Regex.Replace(title, split, "");
                                        break;
                                    }
                                }
                                // 去除多页时通常使用的分隔
                                samiltitle = samiltitle.Replace("-", "").Trim();

                                // 准备信息
                                DownloadInfo info = new DownloadInfo();
                                info.imageUrl = url;
                                info.baseUrl = orgbaseurl;
                                info.title = title;
                                info.samilTitle = samiltitle;
                                info.workUrl = workurl;
                                imageList.Add(url, info);
                                downloadList.Enqueue(url);

                                lock (addToViewList)
                                {
                                    addToViewList.Add(url);
                                }
                            }
                        }
                        else
                        {
                            //lock(linkList)
                            {
                                if (config.UrlKeyWords != null && -1 == url.IndexOf(config.UrlKeyWords))
                                    continue;
                                if (!linkList.ContainsKey(url))
                                {
                                    linkList.Add(url, null);
                                    lock (workList)
                                    {
                                        workList.Enqueue(url);
                                    }
                                }
                            }

                        }

                    }
                }
                catch (Exception e)
                {
                    WriteLog("Parser fialed:" + e.ToString());
                }
            }
            WriteLog("Parser Thread is end. id is:" + Thread.CurrentThread.ManagedThreadId);
        }

        public void DownloadImageLoop()
        {
            WebClient mywebclient = new WebClient();
            WriteLog("Download Thread is starting. id is:" + Thread.CurrentThread.ManagedThreadId);
            if (!Directory.Exists("D:\\WebDownload"))
                Directory.CreateDirectory("D:\\WebDownload");

            string downurl = "";
            DownloadInfo downinfo = null;
            int trytag = 0;
            while (true)
            {
                if (!AppRunning)
                    break;

                var status = threadList[Thread.CurrentThread.ManagedThreadId];
                status.isBusy = false;
                if (!bIsStartWork)
                {
                    Thread.CurrentThread.Suspend();
                }

                try
                {
                    if (workList.Count <= 0 && downloadList.Count <= 0)
                    {
                        if (trytag <= 5)
                        {
                            trytag++;
                            Thread.Sleep(500);
                            continue;
                        }
                        else
                            break;
                    }
                    lock (downloadList)
                    {
                        if (downloadList.Count == 0)
                        {
                            Thread.Sleep(500);
                            continue;
                        }

                        downurl = downloadList.Dequeue();
                        downinfo = imageList[downurl];
                    }

                    status.isBusy = true;
                    status.workURL = downurl;

                    var filepath = GetImageSavePath(downinfo.imageUrl);
                    // 如果文件已经存在,则认为已经下载过.直接跳过
                    if (File.Exists(filepath))
                        continue;

                    try
                    {
                        Byte[] imgdata = mywebclient.DownloadData(downinfo.imageUrl);
                        MemoryStream ms = new MemoryStream(imgdata);
                        Image img = Image.FromStream(ms);
                        downinfo.imgSize = img.Size;
                        if (img.Width > 400 && img.Height > 400)
                        {
                            var savefolder = GetImageSaveFolder(downinfo.imageUrl);
                            try
                            {
                                if (!Directory.Exists(savefolder))
                                    Directory.CreateDirectory(savefolder);
                            }
                            catch(Exception ex)
                            {
                                WriteLog("Create Directory Fialed:" + ex.ToString() + "\r\nThe folder is :" + savefolder);
                            }
                           

                            try
                            {
                                img.Save(filepath);
                            }
                            catch (Exception exp)
                            {
                                WriteLog("Save Image error:" + exp.Message.ToString() + "\r\nThe url is :" + downinfo.workUrl + "\r\nThe save path is:"
                                    + filepath);

                                lock (downloadList)
                                {
                                    downloadList.Enqueue(downurl);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        if( downinfo.retryNum < 10 )
                        {
                            downinfo.retryNum++;
                            downloadList.Enqueue(downurl);
                        }
                        else
                        {
                            WriteLog("Download image failed:" + ex.ToString() + "\r\nThe URL is :" + downurl);
                        }
                    }

                }
                catch (Exception e)
                {
                    WriteLog("Download image failed:" + e.Message.ToString());
                    
                }
            }

            WriteLog("Download Thread is end. id is:" + Thread.CurrentThread.ManagedThreadId);
        }

        public string GetImageTempPath(string imgurl)
        {
            var downinfo = imageList[imgurl];

            string filename = downinfo.imageUrl.Substring(7);
            string filepath = GetImageTempFolder(imgurl) + filename;
            filepath = filepath.Replace("/", "_");
            return filepath;
        }

        public string GetImageTempFolder(string imgurl)
        {
            var downinfo = imageList[imgurl];

            string savepath = Path.GetTempPath() + "\\SLOONG.COM\\WebDownload\\Temp\\Preview\\" + downinfo.baseUrl.Substring(7) + "\\" + downinfo.samilTitle + "\\";
            string filename = downinfo.imageUrl.Substring(7);
            savepath = savepath.Replace("/", "_");
            return savepath;
        }

        public string GetImageSavePath(string imgurl)
        {
            var downinfo = imageList[imgurl];

            string filename = downinfo.imageUrl.Substring(7);
            CheckFileName(ref filename);
            string filepath = GetImageSaveFolder(imgurl) + filename;
            return filepath;
        }

        public string GetImageSaveFolder(string imgurl)
        {
            var downinfo = imageList[imgurl];

            string savepath = "D:\\WebDownload\\" + downinfo.baseUrl.Substring(7) + "\\" + downinfo.samilTitle + "\\";
            string filename = downinfo.imageUrl.Substring(7);
            CheckPath(ref savepath);
            return savepath;
        }

        public void CheckPath(ref string path)
        {
            foreach (var item in pathRep)
            {
                path = path.Replace(item, '_');
            }
        }

        public void CheckFileName( ref string name )
        {
            foreach (var item in nameRep)
            {
                name = name.Replace(item, '_');
            }
        }

        public void WriteLog(string log)
        {
            try
            {
                using (FileStream fs = new FileStream("D:\\WebDownload.log", FileMode.Append))
                {
                    StreamWriter sw = new StreamWriter(fs, Encoding.Default);
                    log = string.Format("[{0}]:{1}", DateTime.Now.ToString(), log);
                    sw.WriteLine(log);
                    sw.Close();
                    fs.Close();
                }
            }
            catch (Exception)
            { }
        }

        public void StartWork()
        {
            bIsStartWork = true;

            WriteLog("Start work");

            buttonStart.Text = "Stop";


            linkList.Clear();
            workList.Clear();
            threadList.Clear();
            panelStatus.Controls.Clear();

            if (config.useCache && Directory.Exists(config.cachepath))
            {
                try
                {
                    if (File.Exists(config.cachepath + "\\imagelist.bin"))
                        imageList = imageList.Deserializer(config.cachepath + "\\imagelist.bin");

                    if (File.Exists(config.cachepath + "\\linklist.bin"))
                        linkList = linkList.Deserializer(config.cachepath + "\\linklist.bin");


                    if (File.Exists(config.cachepath + "\\downloadlist.bin"))
                    {
                        BinaryFormatter formatter = new BinaryFormatter();
                        FileStream stream = new FileStream(config.cachepath + "\\downloadlist.bin", FileMode.Open, FileAccess.Read);
                        downloadList = (Queue<string>)formatter.Deserialize(stream);
                        stream.Close();
                    }

                    if (File.Exists(config.cachepath + "\\worklist.bin"))
                    {
                        BinaryFormatter formatter = new BinaryFormatter();
                        FileStream stream = new FileStream(config.cachepath + "\\worklist.bin", FileMode.Open, FileAccess.Read);
                        workList = (Queue<string>)formatter.Deserialize(stream);
                        stream.Close();
                    }

                    addToViewList.AddRange(imageList.Keys);
                }
                catch (Exception ex)
                {
                    WriteLog("反序列化失败,错误消息:" + ex.ToString() + "\r\n删除所有序列化文件并清空队列.");
                    imageList.Clear();
                    linkList.Clear();
                    downloadList.Clear();
                    workList.Clear();
                }
            }
            else
            {
                if (Directory.Exists("cache"))
                    Directory.Delete("cache", true);
            }


            if (workList.Count == 0) workList.Enqueue(config.workUrl);
            if (linkList.Count == 0) linkList.Add(config.workUrl, null);

            baseurl = config.baseUrl;

            int num = 0;
            try
            {
                num = Convert.ToInt32(comboBoxThreadNum.Text);
            }
            catch (Exception)
            {
                num = 1;
            }

            for (int i = 0; i < num; i++)
            {
                var down = new ThreadStatus();
                down.thread = new Thread(() => DownloadImageLoop());
                down.status = new Label();
                down.thread.Name = "Download Thread";
                threadList.Add(down.thread.ManagedThreadId,down);

                

                var pars = new ThreadStatus();
                pars.thread = new Thread(() => ParserLoop());
                pars.thread.Name = "Parser Thread";
                pars.status = new Label();
                threadList.Add(pars.thread.ManagedThreadId,pars);
            }

            foreach (var item in threadList.Values)
            {
                switch (item.thread.ThreadState)
                {
                    case ThreadState.Unstarted:
                        item.thread.Start();
                        break;
                    case ThreadState.Suspended:
                        item.thread.Resume();
                        break;
                    default:

                        break;
                }

            }

            // process thread status in panel status
            foreach (var item in threadList.Values)
            {
                Label temp = item.status;
                int x = /*panelStatus.Location.X +*/ panelStatus.Controls.Count * 50;
                int y = /*panelStatus.Location.Y +*/ 0;
                temp.Location = new Point(x, y);
                temp.Size = new Size(50, panelStatus.Height);
                panelStatus.Controls.Add(temp);
                temp.Show();
            }
        }

        public void StopWork()
        {
            bIsStartWork = false;

            WriteLog("Stop work");

            buttonStart.Text = "Start";

            bool bIsRunning = false;
            do
            {
                foreach (var item in threadList.Values)
                {
                    if (item.thread.IsAlive)
                    {
                        bIsRunning = true;
                        continue;
                    }
                }
                bIsRunning = false;
            } while (bIsRunning);

            threadList.Clear();

            if (config != null && config.useCache)
            {
                if (!Directory.Exists(config.cachepath))
                    Directory.CreateDirectory(config.cachepath);

                imageList.Serializer(config.cachepath + "\\imagelist.bin");
                linkList.Serializer(config.cachepath + "\\linklist.bin");

                BinaryFormatter formatter = new BinaryFormatter();
                Stream stream = new FileStream(config.cachepath + "\\downloadlist.bin", FileMode.Create, FileAccess.Write);
                formatter.Serialize(stream, downloadList);
                stream.Close();
                stream = new FileStream(config.cachepath + "\\worklist.bin", FileMode.Create, FileAccess.Write);
                formatter.Serialize(stream, workList);
                stream.Close();
            }
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            if (buttonStart.Text == "Start")
                StartWork();

            else
                StopWork();
        }

        private void timerNormal_Tick(object sender, EventArgs e)
        {
            label1.Text = "download list:" + downloadList.Count + ";;;work list:" + workList.Count;

            try
            {
                // Add to view
                List<string> addList = new List<string>();
                lock (addToViewList)
                {
                    if (addToViewList.Count > 0)
                    {
                        addList.AddRange(addToViewList);
                        addToViewList.Clear();
                    }
                }
                if (addList.Count > 0)
                {
                    foreach (var item in addList)
                    {
                        var info = imageList[item];
                        if (!listgroupmap.ContainsKey(info.samilTitle))
                        {
                            ListViewGroup group = new ListViewGroup(info.samilTitle);

                            listViewInfo.Groups.Add(group);
                            listgroupmap.Add(info.samilTitle, group);

                            listViewInfo.SetGroupState(ListViewGroupState.Collapsible | ListViewGroupState.Collapsed, group);
                        }
                        // Create items and add them to myListView.
                        ListViewItem additem = new ListViewItem(new string[] { info.imageUrl }, 0, listgroupmap[info.samilTitle]);
                        listViewInfo.Items.Add(additem);
                    }
                }
            }
            catch (Exception ex)
            {
                WriteLog("in TimeLoop happend error:" + ex.ToString());
            }
        }

        public void RunFuncBackground(DoWorkEventHandler workfunc, object argument)
        {

            BackgroundWorker work = new BackgroundWorker();
            work.DoWork += new DoWorkEventHandler(workfunc);
            work.ProgressChanged += new ProgressChangedEventHandler(work_ProgressChanged);
            work.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
            work.WorkerReportsProgress = true;
            work.RunWorkerAsync(argument);
            form.Location = new Point(listViewInfo.Location.X + listViewInfo.Size.Width / 2, listViewInfo.Location.Y + listViewInfo.Size.Height / 2);
            form.ShowDialog();
        }

        void work_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            form.ProgressValue = e.ProgressPercentage;
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            form.Close();
        }

        private void listView1_Click(object sender, EventArgs e)
        {
            try
            {
                AppRunning = false;

                pictureBoxPrev.Image = null;
                labelSizeText.Text = "";
                ShowPrevArea(true);

                var info = imageList[listViewInfo.FocusedItem.Text];
                linkLabelUrl.Text = info.workUrl;
                linkLabelUrl.Links[0].LinkData = info.workUrl;
                var path = GetImageSavePath(info.imageUrl);
                if (File.Exists(path))
                {
                    // File is download
                    pictureBoxPrev.LoadAsync(path);
                    buttonSave.Hide();
                    linkLabellocalUrl.Text = path;
                    linkLabellocalUrl.Links[0].LinkData = path;
                }
                else
                {
                    if (!Directory.Exists(GetImageTempFolder(info.imageUrl)))
                        Directory.CreateDirectory(GetImageTempFolder(info.imageUrl));

                    RunFuncBackground(DownloadFile, new string[] { info.imageUrl, GetImageTempPath(info.imageUrl) });
                    pictureBoxPrev.Load("Cache\\Cache.jpg");
                    buttonSave.Show();
                }
                if (info.imgSize.Width == 0 || info.imgSize.Height == 0)
                {
                    info.imgSize = pictureBoxPrev.Image.Size;
                }
                labelSizeText.Text = info.imgSize.ToString();
            }
            catch (Exception ex)
            {
                WriteLog("error in listview click, error message:" + ex.ToString());
            }


        }

        private void listView1_DoubleClick(object sender, EventArgs e)
        {
            FormPreview pre = new FormPreview(listViewInfo.FocusedItem.Text);
            pre.ShowDialog();
        }

        private void buttonSave_Click(object sender, EventArgs e)
        {
            var path = GetImageSavePath(linkLabelUrl.Text);
            pictureBoxPrev.Image.Save(path);
            linkLabellocalUrl.Text = path;
        }

        private void linkLabelUrl_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start(e.Link.LinkData.ToString());
        }

        private void linkLabellocalUrl_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start(e.Link.LinkData.ToString());
        }

        private void FormMain_Click(object sender, EventArgs e)
        {
            ShowPrevArea(false);
        }

        public void ShowPrevArea(bool bShow = false)
        {
            if (bShow)
            {
                if (panelPrev.Visible)
                    return;
                panelPrev.Location = PrevLoc;
                panelPrev.Size = PrevSize;
                panelPrev.Show();
                panelPrev.Enabled = true;
                this.Width += panelPrev.Width;
            }
            else
            {
                if (!panelPrev.Visible)
                    return;
                PrevLoc = panelPrev.Location;
                PrevSize = panelPrev.Size;
                panelPrev.Hide();
                panelPrev.Enabled = false;
                this.Width -= panelPrev.Width;
            }
        }

        private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            AppRunning = false;
            bool bIsRunning = false;
            do
            {
                foreach (var item in threadList.Values)
                {
                    if (item.thread.IsAlive)
                    {
                        bIsRunning = true;
                        continue;
                    }
                }
                bIsRunning = false;
            } while (bIsRunning);
        }

        public void OpenConfig(string path)
        {
            if (bIsStartWork)
                StopWork();

            linkList.Clear();
            imageList.Clear();
            workList.Clear();
            downloadList.Clear();
            listgroupmap.Clear();

            listViewInfo.Groups.Clear();
            listViewInfo.Items.Clear();

            listViewInfo.View = View.Details;

            listViewInfo.SetGroupState(ListViewGroupState.Collapsible);

            BinaryFormatter formatter = new BinaryFormatter();
            FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
            config = (DownloadConfig)formatter.Deserialize(stream);
            stream.Close();

            labelInfo.Text = config.workUrl;

            CurrentConfigPath = path;

            FileInfo fi = new FileInfo(path);
            this.Text = fi.Name + " - WebDownload";
        }

        public string GetRealURL(string text, string baseurl, string cururl)
        {
            string url = text;
            char tag = url[0];
            int start, end;

            switch (tag)
            {
                case '\"':
                    start = url.IndexOf("\"", 0);
                    end = url.IndexOf("\"", start + 1);
                    if (start == -1 || end == -1)
                        return "";
                    url = url.Substring(start + 1, end - start - 1);
                    break;
                case '\'':
                    start = url.IndexOf("\'", 0);
                    end = url.IndexOf("\'", start + 1);
                    if (start == -1 || end == -1)
                        return "";
                    url = url.Substring(start + 1, end - start - 1);
                    break;
            }

            if (string.IsNullOrEmpty(url))
                return "";

            // 支持全局相对路径
            if (url[0] == '/')
            {
                url = baseurl + url;
            }
            // 过滤跳转标记和javascript
            else if (url[0] == '#' || url.IndexOf("javascript") != -1)
            {
                return "";
            }
            // 没有http认为是当前相对路径
            else if (-1 == url.IndexOf("http"))
            {
                url = cururl.Substring(0, cururl.LastIndexOf("/") + 1) + url;
            }

            return url;
        }

        public void DownloadFile(object sender, DoWorkEventArgs e)
        {
            string[] args = e.Argument as string[];
            if (args != null)
            {
                DownloadFile(args[0], args[1], sender as BackgroundWorker);
            }
        }

        public void DownloadFile(string URL, string filename, BackgroundWorker worker)
        {
            try
            {
                FileInfo fi = new FileInfo(filename);
                if (!Directory.Exists(fi.DirectoryName))
                    Directory.CreateDirectory(fi.DirectoryName);

                System.Net.HttpWebRequest Myrq = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(URL);
                System.Net.HttpWebResponse myrp = (System.Net.HttpWebResponse)Myrq.GetResponse();
                long totalBytes = myrp.ContentLength;
                System.IO.Stream st = myrp.GetResponseStream();
                System.IO.Stream so = new System.IO.FileStream(filename, System.IO.FileMode.Create);
                long totalDownloadedByte = 0;
                byte[] by = new byte[1024];
                int osize = st.Read(by, 0, (int)by.Length);
                while (osize > 0)
                {
                    totalDownloadedByte = osize + totalDownloadedByte;
                    so.Write(by, 0, osize);
                    if (worker != null)
                    {
                        worker.ReportProgress((int)(totalDownloadedByte / totalBytes * 100));
                    }
                    osize = st.Read(by, 0, (int)by.Length);
                }
                so.Close();
                st.Close();
            }
            catch (System.Exception e)
            {
                MessageBox.Show("获取图片失败!\r\n错误消息:" + e.Message.ToString() + "\r\n目标地址:" + URL);
                return;
            }
        }

        private void buttonEdit_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(CurrentConfigPath))
            {
                return;
            }
            FormConfigEdit wiz = new FormConfigEdit(CurrentConfigPath);
            if (DialogResult.OK == wiz.ShowDialog())
            {
                OpenConfig(CurrentConfigPath);
            }
        }

        private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            System.Environment.Exit(0);
        }

        private void timerFast_Tick(object sender, EventArgs e)
        {
            // process thread status in panel status
            foreach (var item in threadList.Values)
            {
                item.status.Text = item.isBusy == true ? "Busy" : "Free";
                item.status.Update();
            }
        }
    }

    public class DownloadInfo
    {
        public string imageUrl;
        public string title;
        public string samilTitle;
        public string baseUrl;
        public string workUrl;
        public Size imgSize;
        public int retryNum;
    }
}




              /* public static HtmlAndCookie getHtmlAndCookie(string v, string url, Method m, CookieContainer cookie, string Referer, WebProxy proxy, Encoding encode)
        {

            //创建一个HTTP请求 
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.AllowAutoRedirect = false;
            //设置一个代理 
            if (proxy != null)
                req.Proxy = proxy;
            //req.Proxy = myproxy;
            //获取代理服务器的回应
            if (cookie == null) cookie = new CookieContainer();
            req.CookieContainer = cookie;
            //if (!string.IsNullOrEmpty(cookieString))
            //    req.CookieContainer.SetCookies(new Uri("http://www.cmfu.com"), cookieString);
            if (m == Method.Get)
                req.Method = "Get";
            else
                req.Method = "Post";
           
            if (!string.IsNullOrEmpty(Referer))
                req.Referer = Referer;                
            if (encode == null)
                    encode = Encoding.GetEncoding("GB2312");
            if (m==Method.Post)
            {

                byte[] b = encode.GetBytes(v);
                req.ContentLength = b.Length;
                System.IO.Stream newStream = req.GetRequestStream();
                newStream.Write(b, 0, b.Length);
                //newStream.Flush();
                newStream.Close();
            }
            
            HttpWebResponse res = null;
            try
            {
                res = (HttpWebResponse)req.GetResponse();
            }
            catch
            {
                //MessageBox.Show("无法连接服务器！");
                return null;
            }
            //判断HTTP响应状态 
            if (res.StatusCode != HttpStatusCode.OK)
            {
                //richTextBox1.Invoke(new richWrite(richWrite_f), new object[] { "访问失败！" });

                HtmlAndCookie hac = new HtmlAndCookie();
                hac.GotoUrl = res.Headers["Location"];
                hac.HttpStatusCode = res.StatusCode;
                hac.CookieContainer = req.CookieContainer;
                res.Close();
                return hac;
            }
            //获取应答流 
            System.IO.Stream stream = res.GetResponseStream();
            //if(!string.IsNullOrEmpty(res.CharacterSet))
            //{
            //    encode=Encoding.GetEncoding(res.CharacterSet);
            //}
            System.IO.StreamReader r = new System.IO.StreamReader(stream,encode);
            string reV = r.ReadToEnd();
            stream.Close();
            res.Close();
            HtmlAndCookie h = new HtmlAndCookie();
            h.CookieContainer = req.CookieContainer;
            h.Html = reV;
            return h;*/

       